/*
 * @brief Sensor Hub interface module (implementation file)
 *
 * @note
 * Copyright(C) NXP Semiconductors, 2015
 * All rights reserved.
 *
 * @par
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * LPC products.  This software is supplied "AS IS" without any warranties of
 * any kind, and NXP Semiconductors and its licensor disclaim any and
 * all warranties, express or implied, including all implied warranties of
 * merchantability, fitness for a particular purpose and non-infringement of
 * intellectual property rights.  NXP Semiconductors assumes no responsibility
 * or liability for the use of the software, conveys no license or rights under any
 * patent, copyright, mask work right, or any other intellectual property rights in
 * or to any products. NXP Semiconductors reserves the right to make changes
 * in the software without notification. NXP Semiconductors also makes no
 * representation or warranty that such application will be suitable for the
 * specified use without further testing or modification.
 *
 * @par
 * Permission to use, copy, modify, and distribute this software and its
 * documentation is hereby granted, under NXP Semiconductors' and its
 * licensor's relevant copyrights in the software, without fee, provided that it
 * is used in conjunction with NXP Semiconductors microcontrollers.  This
 * copyright, permission, and disclaimer notice must appear in all copies of
 * this code.
 */

#include <stdio.h>
#include <process.h>
#include <stdint.h>
#include <stdbool.h>
#include <windows.h>

#include "lpcusbsio.h"
#include "hostif_protocol.h"

#include "trace.h"
#include "stat_acc.h"
#include "recorder.h"
#include "i2c_io.h"

/*****************************************************************************
 * Private types/enumerations/variables
 ****************************************************************************/
static LPC_HANDLE	g_hSIOPort = NULL;
static LPC_HANDLE	g_hI2CPort = NULL;

static uint8_t		g_RxBuff[MAX_BUFFER_LEN];
static uint8_t		g_TxBuff[MAX_BUFFER_LEN];

/*****************************************************************************
 * Public types/enumerations/variables
 ****************************************************************************/

/*****************************************************************************
 * Private functions
 ****************************************************************************/
static void init_sio(I2C_SIO_CMD_T* sio, uint8_t opt)
{
	memset(g_TxBuff, 0, MAX_BUFFER_LEN);										/* clear tx buffer */
	memset(g_RxBuff, 0, MAX_BUFFER_LEN);										/* clear rx buffer */

	sio->txdata		= g_TxBuff;													/* set tx data pointer */
	sio->txlen		= MAX_BUFFER_LEN;											/* set max buffer size */
	sio->rxdata		= g_RxBuff;													/* set rx buffer */
	sio->rxlen		= MAX_BUFFER_LEN;											/* set rx buffer size */
	sio->options	= opt;														/* set options */
}

static void err_exit(uint32_t tx, uint32_t rx)
{
	I2C_Reset(g_hI2CPort);														/* reset I2C port, and... */
	trace(TRACE_INFO, "TX:%d / RX:%d\n", tx, rx);
	if (trace_level_get() & TRACE_LOGGING) stat_acc_dump();						/* print buffer information (if trace level is set) */
	exit(1);																	/* exit the program */
}

static int32_t send_command(I2C_SIO_CMD_T* sio)
{
	int32_t				res = LPCUSBSIO_OK;
	I2C_FAST_XFER_T		xfer;

	if (g_hI2CPort == NULL) {													/* test for a valid handle before tramission */
		trace(TRACE_INFO,"Invalid I2C handle");									/* trace the error */
		exit(1);																/* if it's bad: exit */
	}

	memset(&xfer, 0, sizeof(I2C_FAST_XFER_T));									/* clear the transmit structure */
	xfer.txBuff		= sio->txdata;												/* copy the data */
	xfer.txSz		= sio->txlen;
	xfer.rxBuff		= sio->rxdata;
	xfer.rxSz		= sio->rxlen;
	xfer.options	= sio->options;
	xfer.slaveAddr	= SENSOR_HUB_I2C_ADDR;
	res = I2C_FastXfer(g_hI2CPort, &xfer);										/* Send the data */

	switch (res) {																/* switch on the transfer response (looking for errors */
	case LPCUSBSIO_ERR_TIMEOUT:
		trace(TRACE_INFO,"Time-out error");										/* trace it */
		err_exit(sio->txlen, sio->rxlen);										/* error exit */
		break;
	case LPCUSBSIO_ERR_I2C_SLAVE_NAK:
		trace(TRACE_INFO,"I2C slave NAK");										/* trace it */
		err_exit(sio->txlen, sio->rxlen);										/* error exit */
		break;
	case LPCUSBSIO_ERR_HID_LIB:
		trace(TRACE_INFO,"fatal error:%d",res);									/* trace it */
		err_exit(sio->txlen, sio->rxlen);										/* error exit */
		break;
	}

	if (sio->rxlen == 0)														/* If we aren't expecting anything */
		return 0;																/* return 0 */

	if (res != sio->rxlen)														/* if function received the correct amount of data */
		trace(TRACE_INFO,"length error:%d %d",res,sio->rxlen);					/* ...trace it */

	return res;
}

/*****************************************************************************
 * Public functions
 ****************************************************************************/
LPC_HANDLE	i2c_sd_get_sio_handle(void)		{	return g_hSIOPort;	}
LPC_HANDLE	i2c_sd_get_i2c_handle(void)		{	return g_hI2CPort;	}

void i2c_sd_init(void)
{
	trace_enter();
	g_hSIOPort		= NULL;
	g_hI2CPort		= NULL;

	memset(g_TxBuff, 0, MAX_BUFFER_LEN);
	memset(g_RxBuff, 0, MAX_BUFFER_LEN);

	stat_acc_init();
	trace_exit();
}

bool i2c_sd_open(void)
{
	I2C_PORTCONFIG_T		cfgParam;

	trace_enter();
	trace(TRACE_INFO,"Open SIO port");
	g_hSIOPort = LPCUSBSIO_Open(0);
	if (g_hSIOPort == NULL) {
		trace(TRACE_INFO,"Invalid SIO handle");
		exit(1);
	}

	cfgParam.ClockRate = I2C_CLOCK_FAST_MODE;
	cfgParam.Options = 0;
	trace(TRACE_INFO,"Open I2C port");
	g_hI2CPort = I2C_Open(g_hSIOPort, &cfgParam, 0);

	if (g_hI2CPort == NULL) {
		trace(TRACE_INFO,"Invalid I2C handle");
		exit(1);
	}

	trace_exit();
	return true;
}
void i2c_sd_close(void)
{
	trace_enter();
	I2C_Close(g_hI2CPort);
	LPCUSBSIO_Close(0);
	i2c_sd_init();
	trace_exit();
}

bool i2c_sd_sensor_reset(void)
{
	I2C_SIO_CMD_T sio;
	struct ShHubCmdHeader_t* param;
	bool s_state;

	trace_enter();
	init_sio(&sio,I2C_FAST_XFER_OPTION_IGNORE_NACK);
	param = (struct ShHubCmdHeader_t*)sio.txdata;
	param->command = LPCSH_CMD_RESET;
	sio.txlen = 1;
	sio.rxlen = 0;
	s_state = (send_command(&sio) < 0) ? false : true;
	trace_exit();

	return s_state;
}

bool i2c_sd_sensor_batch(uint8_t sensor_id, bool enable)
{
	struct ShSensorSetCmdHeader_batch_t*	batchCmd;
	I2C_SIO_CMD_T		sio;
	bool				r_val = true;

	trace_enter();																/* trace the function */
	init_sio(&sio,I2C_FAST_XFER_OPTION_IGNORE_NACK);							/* initialize the structure */
	batchCmd = (struct ShSensorSetCmdHeader_batch_t*)sio.txdata;				/* point the batch command at tx data block */

	batchCmd->command = LPCSH_CMD_SET_BATCH;									/* set the command */
	batchCmd->sensorId = sensor_id;												/* set the sensor ID */
	batchCmd->flags = 0;														/* set the flags */
	batchCmd->period_us = (enable)? 20000 : 0;									/* 20msec so we should get data at 50Hz */
	batchCmd->timeout_us = 0; 													/* 0 latency data to be sent immediately */

	sio.txlen = sizeof(struct ShSensorSetCmdHeader_batch_t);					/* set the tx data size */
	sio.rxlen = 0;																/* set the rx data size */
	send_command(&sio);

	trace_exit();

	return r_val;
}

bool i2c_sd_sensor_enable(uint8_t sensor_id, bool enable)
{
	I2C_SIO_CMD_T sio;
	struct ShSensorSetCmdHeader_8bits_param_t* param;
	bool r_val = true;

	trace_enter();
	init_sio(&sio,I2C_FAST_XFER_OPTION_IGNORE_NACK);
	param = (struct ShSensorSetCmdHeader_8bits_param_t*)sio.txdata;
	param->command = LPCSH_CMD_SENSOR_ENABLE;
	param->sensorId = sensor_id;
	param->param = (enable) ? 0xFF : 0x00;
	sio.txlen = 3;
	sio.rxlen = 0;
	send_command(&sio);
	trace_exit();

	return r_val;
}

int32_t i2c_sd_who_am_i(void)
{
	int32_t			res = 0;
	I2C_SIO_CMD_T	sio;

	init_sio(&sio, I2C_FAST_XFER_OPTION_IGNORE_NACK);							/* Initialize SIO command block */
	sio.txdata[0] = LPCSH_CMD_WHO_AM_I;											/* Set new command: get the data */
	sio.txlen = 1;																/* Set transmit length */
	sio.rxlen = 1;																/* Set RX length */
	res = send_command(&sio);													/* Get the data */
	if (res == 1)																/* If result is correct */
		res = g_RxBuff[0];														/* Copy the data to the return value */

	return res;
}

uint16_t i2c_sd_get_version(void)
{
	struct ShHubCmdHeader_t* param;
	I2C_SIO_CMD_T sio;
	uint16_t version;
	int32_t res = 0;

	init_sio(&sio, I2C_FAST_XFER_OPTION_IGNORE_NACK);							/* Initialize the SIO command block */
	param = (struct ShHubCmdHeader_t*)sio.txdata;								/* Point to the command block tx buffer with param */
	param->command = LPCSH_CMD_GET_VERSION;										/* Get version from embedded */
	sio.txlen = 1;																/* Set transmit length (single byte) */
	sio.rxlen = 2;																/* Set receive data size (expected) */
	res = send_command(&sio);													/* Send the data */
	if (res == 2)																/* if 2 bytes were returned */
		version = sio.rxdata[0]<<8 | sio.rxdata[1];								/* save version */
	else																		/* if not... */
		version = 0;															/* default version to 0 */

	return version;																/* return the data length */
}


uint16_t i2c_sd_get_data_len(void)
{
	struct ShHubCmdHeader_t* param;
	I2C_SIO_CMD_T sio;
	uint16_t data_len;

	init_sio(&sio, I2C_FAST_XFER_OPTION_IGNORE_NACK);							/* Initialize the SIO command block */
	param = (struct ShHubCmdHeader_t*)sio.txdata;								/* Point to the command block tx buffer with param */
	param->command = LPCSH_CMD_GET_DATA_LENGTH;									/* Get data length from embedded */
	sio.txlen = 1;																/* Set transmit length (single byte) */
	sio.rxlen = 2;																/* Set receive data size (expected) */
	data_len = (send_command(&sio) == 2) ? (uint16_t)(sio.rxdata[0]) : 0;		/* test for correct data size */

	return data_len;															/* return the data length */
}

bool i2c_sd_get_data(uint8_t* dest_ptr, uint16_t datalen)
{
	int32_t			res = 0;
	I2C_SIO_CMD_T	sio;

	init_sio(&sio, I2C_FAST_XFER_OPTION_IGNORE_NACK);							/* Initialize SIO command block */
	sio.txdata[0] = LPCSH_CMD_GET_DATA;											/* Set new command: get the data */
	sio.txlen = 1;																/* Set transmit length */
	sio.rxlen = datalen;														/* Set RX length */
	res = send_command(&sio);													/* Get the data */
	memcpy(dest_ptr, g_RxBuff, datalen);										/* Copy the data to the buffer */

	if (rec_get_flag() == true)
		rec_write_block(g_RxBuff, datalen);

	stat_acc_process(datalen);													/* accumulate statistics */

	return res;
}
